import { Extent } from "ol/extent";
import { getMap } from "./instance";
import WMTSTileGrid from "ol/tilegrid/WMTS";
import * as Layer from "ol/layer";
import * as Source from "ol/source";
import { createEmpty, getTopLeft, getWidth } from "ol/extent";
import TileState from "ol/TileState";
import { _fetchTileByBlob } from "./utils";
import * as keys from "./keys";
import { get } from "ol/proj";

// 渲染 WMTS 瓦片图层配置项
export interface RenderWMTSLayerOptions {
  type: string; // 图层类型
  className?: string; // 自定义图层 CSS 类名，默认为 ol-layer
  opacity?: number; // 图层透明度，默认为 1
  visible?: boolean; // 图层是否可见，默认为 true
  extent?: Extent; // 渲染边界
  zIndex?: number; // 图层层级
  minResolution?: number; // 最小显示分辨率
  maxResolution?: number; // 最大显示分辨率
  minZoom?: number; // 最小显示缩放层级
  maxZoom?: number; // 最大显示缩放层级
  url: string; // MVT 服务地址
  attributions?: string | string[];
  attributionsCollapsible?: boolean; // 属性是否可折叠，默认为 true
  name: string; // 图层名
  crossOrigin?: string; // 跨源值
  format?: string; // 格式
  style?: string; // 样式
  headers?: Record<string, string>; // 请求头
  projection?: string; // 坐标系
  matrixSet: string; // 矩阵集
  preload?: number; // 预加载，0 表示不预加载，默认为 0
  properties?: Record<string, any>; // 自定义属性
  cacheSize?: number; // 内部图块缓存大小，默认为 0
}
/**
 * @description 渲染 WMTS 瓦片图层
 * @private
 * @param {RenderWMTSLayerOptions} options 配置项
 * @returns {Layer.Vector}
 */
export const renderWMTSTileLayer = (options: RenderWMTSLayerOptions) => {
  const mapInstance = getMap();
  if (!mapInstance) {
    throw new Error("地图实例未初始化");
  }
  const {
    attributions,
    attributionsCollapsible,
    crossOrigin = "anonymous",
    format,
    headers,
    matrixSet,
    name,
    projection = "EPSG:4326",
    style = "default",
    type: layerType,
    url,
    visible = true,
  } = options;
  let layer = mapInstance
    .getAllLayers()
    .find((item) => item.get(keys.LAYER_TYPE_KEY) === layerType) as
    | Layer.Tile
    | undefined;
  if (!(layer instanceof Layer.Tile)) {
    layer = new Layer.Tile({
      // projection:options.projection,
      visible,
    });
    layer.set(keys.LAYER_TYPE_KEY, layerType);
    layer.set(keys.LAYER_FORMAT_KEY, "wmts");
    mapInstance.addLayer(layer);
  }

  const projectionExtent = get(projection)?.getExtent() || createEmpty();
  const size = getWidth(projectionExtent) / 256;
  const resolutions = new Array(19);
  const matrixIds = new Array(19);
  for (let z = 1; z < 19; ++z) {
    resolutions[z] = size / Math.pow(2, z);
    matrixIds[z] = z;
  }

  layer.setSource(
    new Source.WMTS({
      attributions,
      attributionsCollapsible,
      crossOrigin,
      layer: name,
      style,
      tileGrid: new WMTSTileGrid({
        origin: getTopLeft(projectionExtent),
        resolutions,
        matrixIds,
      }),
      tileLoadFunction: (tile, src) => {
        tile.setState(TileState.LOADING);
        _fetchTileByBlob({ url: src, headers })
          .then((blob) => {
            // @ts-ignore
            const image = tile.getImage() as InstanceType<typeof Image>;
            if (image instanceof Image) {
              const _url = URL.createObjectURL(blob);
              image.src = _url;
              image.onload = () => {
                URL.revokeObjectURL(_url);
                tile.setState(TileState.LOADED);
              };
            }
          })
          .catch((e) => {
            tile.setState(TileState.ERROR);
            throw e;
          });
      },
      format,
      matrixSet,
      url,
      wrapX: true,
    })
  );
  layer.setVisible(visible);

  return layer;
};
